package pl.com.qapps.unity;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicLong;

import pl.com.qapps.unity.NotifyPb.NotificationPb;
import pl.com.qapps.unity.NotifyPb.NotifyEvent;
import pl.com.qapps.unity.NotifyPb.NotifyEvent.Type;
import pl.com.qapps.unity.UnityPb.Request;

/**
 * Allows you to show notifications to user using NotifyOsd system.
 * 
 * @author Jakub Dykowski
 * 
 */
public final class NotifyOsd {

	/**
	 * Do NOT use.
	 */
	private NotifyOsd() {
	}

	/**
	 * This is shortcut for creating new {@link Notification} and calling the
	 * {@link Notification#show()} method.
	 * 
	 * @param summary
	 *            the summary, can be <code>null</code>
	 * @param body
	 *            notification content, can be <code>null</code>
	 * @param icon
	 *            the icon path, can be null
	 * @return newly created and shown notification
	 */
	public static Notification notify(String title, String message,
			StockIcon icon) throws NotifyException {
		return notify(title, message, icon != null ? icon.getName() : null);
	}

	/**
	 * This is shortcut for creating new {@link Notification} and calling the
	 * {@link Notification#show()} method.
	 * 
	 * @param summary
	 *            the summary, can be <code>null</code>
	 * @param body
	 *            notification content, can be <code>null</code>
	 * @param icon
	 *            the icon path, can be null
	 * @return newly created and shown notification
	 */
	public static Notification notify(String summary, String body, String icon)
			throws NotifyException {
		return new Notification(summary, body, icon).show();
	}

	private static void send(NotifyEvent evt) throws NotifyException {
		try {
			Unity.connect().send(
					Request.newBuilder()
							.setExtension(NotifyPb.notifyEvent, evt).build());
		} catch (UnityException | IllegalStateException | IOException e) {
			throw new NotifyException(e);
		}
	}

	/**
	 * Represents a notification which can be show in user interface. Can be
	 * used multiple times. Remember to call {@link #show()} method to make the
	 * notification visible.
	 * 
	 * <p>
	 * Note that by default, if notification with the same summary is already
	 * shown, the body will be added to the shown notification instead of
	 * showing another notification.
	 * </p>
	 * 
	 * NotThreadSafe.
	 * 
	 * @author Jakub Dykowski
	 * 
	 */
	public static final class Notification {

		private static AtomicLong count = new AtomicLong(1);
		private final transient long id = count.getAndIncrement();
		private String summary;
		private String body;
		private String icon;
		// TODO this value is meaningless, always appending
		private boolean append = true;

		/**
		 * Stands for serialization requirements. Do not use.
		 */
		@SuppressWarnings("unused")
		private Notification() {
		}

		/**
		 * Creates new {@linkplain Notification} using predefined icon.
		 * 
		 * @param summary
		 *            can be <code>null</code>
		 * @param body
		 *            can be <code>null</code>
		 * @param icon
		 *            can be <code>null</code>
		 */
		public Notification(String title, String message, StockIcon icon)
				throws NotifyException {
			this(title, message, icon.getName());
		}

		/**
		 * Creates new {@linkplain Notification} using given icon path. If icon
		 * is not found no exception is thrown, as a result there will be jus
		 * blank space instead of icon.
		 * 
		 * @param summary
		 * @param body
		 * @param icon
		 */
		public Notification(String summary, String body, String icon)
				throws NotifyException {
			this.summary = summary;
			this.body = body;
			this.icon = icon;
			send(NotifyEvent.newBuilder().setNotification(toProto())
					.setType(Type.CREATE).build());
		}

		/**
		 * Makes the notification appear on user screen.
		 * 
		 * @return the {@linkplain Notification} itself
		 * @throws NotifyException
		 *             if any internal error occurred
		 */
		public Notification show() throws NotifyException {
			send(NotifyEvent.newBuilder().setType(Type.SHOW)
					.setNotification(toProto()).build());
			return this;
		}

		public String getSummary() {
			return summary;
		}

		public Notification setSummary(String summary) {
			this.summary = summary;
			return this;
		}

		public String getBody() {
			return body;
		}

		public Notification setBody(String body) {
			this.body = body;
			return this;
		}

		public String getIcon() {
			return icon;
		}

		public Notification setIcon(String icon) {
			this.icon = icon;
			return this;
		}

		public Notification setIcon(StockIcon icon) {
			this.icon = icon != null ? icon.getName() : null;
			return this;
		}

		// TODO implement appending and add it to API
		// public boolean getAppend() {
		// return append;
		// }
		//
		// /**
		// * <code>false</code> by default. Sets whether body of this message
		// can
		// * be added to already shown notification with the same summary or
		// not.
		// *
		// * @param append
		// * @return
		// */
		// public Notification setAppend(boolean append) {
		// this.append = append;
		// return this;
		// }

		@Override
		public String toString() {
			return "<Notification " + summary + ">";
		}

		private synchronized NotificationPb toProto() {
			NotificationPb.Builder builder = NotificationPb.newBuilder().setId(
					id);
			if (summary != null)
				builder.setSummary(summary);
			if (body != null)
				builder.setBody(body);
			if (icon != null)
				builder.setIcon(icon);
			builder.setAppend(append);
			return builder.build();
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#finalize()
		 */
		@Override
		protected void finalize() throws Throwable {
			send(NotifyEvent.newBuilder().setNotification(toProto())
					.setType(Type.DELETE).build());
		}
	}

	/**
	 * Bunch of predefined system icons, like audio level, wireless network
	 * state or warning icon.
	 * 
	 * @author Jakub Dykowski
	 * 
	 */
	public enum StockIcon {

		AUDIO_NEXT("notification-audio-next"), AUDIO_PLAY(
				"notification-audio-play"), AUDIO_PREVIOUS(
				"notification-audio-previous"), AUDIO_VOLUME_HIGH(
				"notification-audio-volume-high"), AUDIO_VOLUME_LOW(
				"notification-audio-volume-low"), AUDIO_VOLUME_MEDIUM(
				"notification-audio-volume-medium"), AUDIO_VOLUME_MUTED(
				"notification-audio-volume-muted"), AUDIO_VOLUME_OFF(
				"notification-audio-volume-off"), BATTERY_LOW(
				"notification-battery-low"), DEVICE_EJECT(
				"notification-device-eject"), DEVICE_FIREWIRE(
				"notification-device-firewire"), DIAPLAY_BRIGHTNESS_FULL(
				"notification-display-brightness-full"), DISPLAY_BRIGHTNESS_HIGH(
				"notification-display-brightness-high"), DISPLAY_BRIGHTNESS_LOW(
				"notification-display-brightness-low"), DISPLAY_BRIGHTNESS_MEDIUM(
				"notification-display-brightness-medium"), DISPLAY_BRIGHTNESS_OFF(
				"notification-display-brightness-off"), GSM_3G_FULL(
				"notification-gsm-3g-full"), GSM_3G_HIGH(
				"notification-gsm-3g-high"), GSM_3G_LOW(
				"notification-gsm-3g-low"), GSM_3G_MEDIUM(
				"notification-gsm-3g-medium"), GSM_3G_NONE(
				"notification-gsm-3g-none"), GSM_DISCONNECTED(
				"notification-gsm-disconnected"), GSM_EDGE_FULL(
				"notification-gsm-edge-full"), GSM_EDGE_HIGH(
				"notification-gsm-edge-high"), GSM_EDGE_LOW(
				"notification-gsm-edge-low"), GSM_EDGE_MEDIUM(
				"notification-gsm-edge-medium"), GSM_EDGE_NONE(
				"notification-gsm-edge-none"), GSM_FULL("notification-gsm-full"), GSM_H_FULL(
				"notification-gsm-h-full"), GSM_H_HIGH(
				"notification-gsm-h-high"), GSM_HIGH("notification-gsm-high"), GSM_H_MEDIUM(
				"notification-gsm-h-medium"), GSM_H_LOW(
				"notification-gsm-h-low"), GSM_H_NONE("notification-gsm-h-none"), GSM_LOW(
				"notification-gsm-low"), GSM_MEDIUM("notification-gsm-medium"), GSM_NONE(
				"notification-gsm-none"), KEYBOARD_BRIGHTNESS_FULL(
				"notification-keyboard-brightness-full"), KEYBOARD_BRIGHTNESS_HIGH(
				"notification-keyboard-brightness-high"), KEYBOARD_BRIGHTNESS_LOW(
				"notification-keyboard-brightness-low"), KEYBOARD_BRIGHTNESS_MEDIUM(
				"notification-keyboard-brightness-medium"), KEYBOARD_BRIGHTNESS_OFF(
				"notification-keyboard-brightness-off"), MESSAGE_EMAIL(
				"notification-body-email"), MESSAGE_IM("notification-body-im"), NETWORK_ETHERNET_CONNECTED(
				"notification-network-ethernet-connected"), NETWORK_ETHERNET_DISCONNECTED(
				"notification-network-ethernet-disconnected"), NETWORK_WIRELESS_CONNECTED(
				"notification-network-wireless-disconnected"), NETWORK_WIRELESS_FULL(
				"notification-network-wireless-full"), NETWORK_WIRELESS_HIGH(
				"notification-network-wireless-high"), NETWORK_WIRELESS_LOW(
				"notification-network-wireless-low"), NETWORK_WIRELESS_MEDIUM(
				"notification-network-wireless-medium"), NETWORK_WIRELESS_NONE(
				"notification-network-wireless-none"), POWER_DISCONNECTED(
				"notification-power-disconnected"), DIALOG_WARNING(
				"dialog-warning"), DIALOG_QUESTION("dialog-question"), DIALOG_INFORMATION(
				"dialog-information"), DIALOG_ERROR("error-dialog");

		private final String name;

		private StockIcon(String name) {
			this.name = name;
		}

		private String getName() {
			return name;
		}
	}

}
